from sqlalchemy.orm        import MapperExtension, EXT_PASS

class ValidationException(Exception):
    pass

class Validator(MapperExtension):
    def __init__(self, *args):
        MapperExtension.__init__(self)
        self.validations_to_perform = args
    
    def before_insert(self, mapper, connection, instance):
        for validation in self.validations_to_perform:
            if not validation.validate(instance):
                raise ValidationException(validation.get_error_message(instance))
    
        return EXT_PASS
    
    before_update = before_insert

class Validation(object):
    def validate(self):
        raise NotImplementedError
    
    def get_error_message(self):
        raise NotImplementedError

class FieldValidation(Validation):
    def __init__(self, field_to_validate, validation_rule):
        Validation.__init__(self)
        self.field_to_validate = field_to_validate
        self.validation_rule = validation_rule
        
    def validate(self, instance):
        return self.validation_rule(self.get_field_value(instance))

    def get_error_message(self, instance):
        return "Validation of field %s (with rule %s) failed." % (self.field_to_validate, self.validation_rule)
    
    def get_field_value(self, instance):
        return getattr(instance, self.field_to_validate)

class FieldRangeValidation(FieldValidation):
    def __init__(self, field_to_validate, min, max):
        FieldValidation.__init__(self, field_to_validate, self._validate_field)
        self.min = min
        self.max = max
    
    def _validate_field(self, field_value):
        return self.min <= field_value <= self.max
    
    def get_error_message(self, instance):
        return 'Range validation of field "%s" of instance of class "%s" failed! %s.%s = %s (min=%d, max=%d)' % (self.field_to_validate, instance.__class__.__name__, instance.__class__.__name__, self.field_to_validate, self.get_field_value(instance), self.min, self.max)

class FieldFormatValidation(FieldValidation):
    def __init__(self, field_to_validate, regex):
        FieldValidation.__init__(self, field_to_validate, self._validate_field)
        self.regex = regex
    
    def _validate_field(self, field_value):
        return not (self.regex.match(field_value) is None)
    
    def get_error_message(self, instance):
        return 'Format validation of field "%s" of instance of class "%s" failed! %s.%s = %s (regex=%s)' % (self.field_to_validate, instance.__class__.__name__, instance.__class__.__name__, self.field_to_validate, self.get_field_value(instance), self.regex)

class FieldPresenceValidation(FieldValidation):
    def __init__(self, field_to_validate):
        FieldValidation.__init__(self, field_to_validate, self._validate_field)
    
    def _validate_field(self, field_value):
        return not (field_value is None) #TODO: Add better presence validation
    
    def get_error_message(self, instance):
        return 'Presence validation of field "%s" of instance of class "%s" failed! %s.%s = %s' % (self.field_to_validate, instance.__class__.__name__, instance.__class__.__name__, self.field_to_validate, self.get_field_value(instance))


def range_of(field_to_validate, min, max):
    return FieldRangeValidation(field_to_validate, min, max)

def format_of(field_to_validate, regex):
    return FieldFormatValidation(field_to_validate, regex)

def presence_of(field_to_validate):
    return FieldPresenceValidation(field_to_validate)

__all__=['ValidationException', 'Validator', 'range_of', 'format_of', 'presence_of']
